在这个notebook文件中,有些模板代码已经提供给你,但你还需要实现更多的功能来完成这个项目。除非有明确要求,你无须修改任何已给出的代码。以'(练习)'开始的标题表示接下来的代码部分中有你需要实现的功能。这些部分都配有详细的指导,需要实现的部分也会在注释中以'TODO'标出。请仔细阅读所有的提示。
除了实现代码外,你还需要回答一些与项目及代码相关的问题。每个需要回答的问题都会以 '问题 X' 标记。请仔细阅读每个问题,并且在问题后的 '回答' 部分写出完整的答案。我们将根据 你对问题的回答 和 撰写代码实现的功能 来对你提交的项目进行评分。
提示:Code 和 Markdown 区域可通过 Shift + Enter 快捷键运行。此外,Markdown可以通过双击进入编辑模式。
项目中显示为_选做_的部分可以帮助你的项目脱颖而出,而不是仅仅达到通过的最低要求。如果你决定追求更高的挑战,请在此 notebook 中完成_选做_部分的代码。
在这个notebook中,你将迈出第一步,来开发可以作为移动端或 Web应用程序一部分的算法。在这个项目的最后,你的程序将能够把用户提供的任何一个图像作为输入。如果可以从图像中检测到一只狗,它会输出对狗品种的预测。如果图像中是一个人脸,它会预测一个与其最相似的狗的种类。下面这张图展示了完成项目后可能的输出结果。(……实际上我们希望每个学生的输出结果不相同!)

在现实世界中,你需要拼凑一系列的模型来完成不同的任务;举个例子,用来预测狗种类的算法会与预测人类的算法不同。在做项目的过程中,你可能会遇到不少失败的预测,因为并不存在完美的算法和模型。你最终提交的不完美的解决方案也一定会给你带来一个有趣的学习经验!
我们将这个notebook分为不同的步骤,你可以使用下面的链接来浏览此notebook。
在该项目中包含了如下的问题:
在下方的代码单元(cell)中,我们导入了一个狗图像的数据集。我们使用 scikit-learn 库中的 load_files 函数来获取一些变量:
train_files, valid_files, test_files - 包含图像的文件路径的numpy数组train_targets, valid_targets, test_targets - 包含独热编码分类标签的numpy数组dog_names - 由字符串构成的与标签相对应的狗的种类from sklearn.datasets import load_files
from keras.utils import np_utils
import numpy as np
from glob import glob
# define function to load train, test, and validation datasets
def load_dataset(path):
data = load_files(path)
dog_files = np.array(data['filenames'])
dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
return dog_files, dog_targets
# load train, test, and validation datasets
train_files, train_targets = load_dataset('/data/dog_images/train')
valid_files, valid_targets = load_dataset('/data/dog_images/valid')
test_files, test_targets = load_dataset('/data/dog_images/test')
# load list of dog names
dog_names = [item[20:-1] for item in sorted(glob("/data/dog_images/train/*/"))]
# print statistics about the dataset
print('There are %d total dog categories.' % len(dog_names))
print('There are %s total dog images.\n' % len(np.hstack([train_files, valid_files, test_files])))
print('There are %d training dog images.' % len(train_files))
print('There are %d validation dog images.' % len(valid_files))
print('There are %d test dog images.'% len(test_files))
在下方的代码单元中,我们导入人脸图像数据集,文件所在路径存储在名为 human_files 的 numpy 数组。
import random
random.seed(8675309)
# 加载打乱后的人脸数据集的文件名
human_files = np.array(glob("/data/lfw/*/*"))
random.shuffle(human_files)
# 打印数据集的数据量
print('There are %d total human images.' % len(human_files))
我们将使用 OpenCV 中的 Haar feature-based cascade classifiers 来检测图像中的人脸。OpenCV 提供了很多预训练的人脸检测模型,它们以XML文件保存在 github。我们已经下载了其中一个检测模型,并且把它存储在 haarcascades 的目录中。
在如下代码单元中,我们将演示如何使用这个检测模型在样本图像中找到人脸。
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
# 提取预训练的人脸检测模型
face_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_alt.xml')
# 加载彩色(通道顺序为BGR)图像
img = cv2.imread(human_files[3])
# 将BGR图像进行灰度处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 在图像中找出脸
faces = face_cascade.detectMultiScale(gray)
# 打印图像中检测到的脸的个数
print('Number of faces detected:', len(faces))
# 获取每一个所检测到的脸的识别框
for (x,y,w,h) in faces:
# 在人脸图像中绘制出识别框
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
# 将BGR图像转变为RGB图像以打印
cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 展示含有识别框的图像
plt.imshow(cv_rgb)
plt.show()
在使用任何一个检测模型之前,将图像转换为灰度图是常用过程。detectMultiScale 函数使用储存在 face_cascade 中的的数据,对输入的灰度图像进行分类。
在上方的代码中,faces 以 numpy 数组的形式,保存了识别到的面部信息。它其中每一行表示一个被检测到的脸,该数据包括如下四个信息:前两个元素 x、y 代表识别框左上角的 x 和 y 坐标(参照上图,注意 y 坐标的方向和我们默认的方向不同);后两个元素代表识别框在 x 和 y 轴两个方向延伸的长度 w 和 d。
我们可以将这个程序封装为一个函数。该函数的输入为人脸图像的路径,当图像中包含人脸时,该函数返回 True,反之返回 False。该函数定义如下所示。
# 如果img_path路径表示的图像检测到了脸,返回"True"
def face_detector(img_path):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray)
return len(faces) > 0
在下方的代码块中,使用 face_detector 函数,计算:
human_files 的前100张图像中,能够检测到人脸的图像占比多少? -------------100%dog_files 的前100张图像中,能够检测到人脸的图像占比多少? ---------------11%理想情况下,人图像中检测到人脸的概率应当为100%,而狗图像中检测到人脸的概率应该为0%。你会发现我们的算法并非完美,但结果仍然是可以接受的。我们从每个数据集中提取前100个图像的文件路径,并将它们存储在human_files_short和dog_files_short中。
human_files_short = human_files[:100]
dog_files_short = train_files[:100]
## 请不要修改上方代码
## TODO: 基于human_files_short和dog_files_short
## 中的图像测试face_detector的表现
dog_faces_count = np.mean([face_detector(i) for i in dog_files_short])
print (dog_faces_count)
human_faces_count = np.mean(list(map(face_detector, human_files_short)))
print (human_faces_count)
human_count = []
dog_count = []
for i in human_files_short:
if face_detector(i):
human_count.append(i)
for i in dog_files_short:
if face_detector(i):
dog_count.append(i)
print(len(human_count))
print(len(dog_count))
## (选做) TODO: 报告另一个面部检测算法在LFW数据集上的表现
### 你可以随意使用所需的代码单元数
import mtcnn
from mtcnn.mtcnn import MTCNN
def face_detector2(img_path):
pixels = plt.imread(img_path)
detector = MTCNN()
faces = detector.detect_faces(pixels)
return len(faces) > 0
h2 = []
d2 = []
for i in human_files_short:
if face_detector2(i):
h2.append(i)
for i in dog_files_short:
if face_detector2(i):
d2.append(i)
print (len(h2))
print (len(d2))
# worse than cv2!!!
# have a look what pictures recongnized as human face with mtcnn
for i in d2:
pixels = plt.imread(i)
plt.imshow(pixels)
plt.show()
from matplotlib.patches import Rectangle
def draw_image_with_boxes(file_path, faces):
pixels = plt.imread(file_path)
plt.imshow(pixels)
ax = plt.gca()
for f in faces:
x, y, width, height = f['box']
rect = Rectangle((x, y), width, height, fill=False, color='red')
ax.add_patch(rect)
plt.show()
for i in d2:
pixels = plt.imread(i)
detector = MTCNN()
faces = detector.detect_faces(pixels)
draw_image_with_boxes(i, faces)
在这个部分中,我们使用预训练的 ResNet-50 模型去检测图像中的狗。下方的第一行代码就是下载了 ResNet-50 模型的网络结构参数,以及基于 ImageNet 数据集的预训练权重。
ImageNet 这目前一个非常流行的数据集,常被用来测试图像分类等计算机视觉任务相关的算法。它包含超过一千万个 URL,每一个都链接到 1000 categories 中所对应的一个物体的图像。任给输入一个图像,该 ResNet-50 模型会返回一个对图像中物体的预测结果。
from keras.applications.resnet50 import ResNet50
# 定义ResNet50模型
ResNet50_model = ResNet50(weights='imagenet')
(nb_samples, rows, columns, channels)。其中 nb_samples 表示图像(或者样本)的总数,rows, columns, 和 channels 分别表示图像的行数、列数和通道数。path_to_tensor 函数实现如下将彩色图像的字符串型的文件路径作为输入,返回一个4维张量,作为 Keras CNN 输入。因为我们的输入图像是彩色图像,因此它们具有三个通道( channels 为 3)。(1, 224, 224, 3)。paths_to_tensor 函数将图像路径的字符串组成的 numpy 数组作为输入,并返回一个4维张量,各维度尺寸为 (nb_samples, 224, 224, 3)。 在这里,nb_samples是提供的图像路径的数据中的样本数量或图像数量。你也可以将 nb_samples 理解为数据集中3维张量的个数(每个3维张量表示一个不同的图像。from keras.preprocessing import image
from tqdm import tqdm
def path_to_tensor(img_path):
# 用PIL加载RGB图像为PIL.Image.Image类型
img = image.load_img(img_path, target_size=(224, 224))
# 将PIL.Image.Image类型转化为格式为(224, 224, 3)的3维张量
x = image.img_to_array(img)
# 将3维张量转化为格式为(1, 224, 224, 3)的4维张量并返回
return np.expand_dims(x, axis=0)
def paths_to_tensor(img_paths):
list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
return np.vstack(list_of_tensors)
对于通过上述步骤得到的四维张量,在把它们输入到 ResNet-50 网络、或 Keras 中其他类似的预训练模型之前,还需要进行一些额外的处理:
[103.939, 116.779, 123.68](以 RGB 模式表示,根据所有的 ImageNet 图像算出)。导入的 preprocess_input 函数实现了这些功能。如果你对此很感兴趣,可以在 这里 查看 preprocess_input的代码。
在实现了图像处理的部分之后,我们就可以使用模型来进行预测。这一步通过 predict 方法来实现,它返回一个向量,向量的第 i 个元素表示该图像属于第 i 个 ImageNet 类别的概率。这通过如下的 ResNet50_predict_labels 函数实现。
通过对预测出的向量取用 argmax 函数(找到有最大概率值的下标序号),我们可以得到一个整数,即模型预测到的物体的类别。进而根据这个 清单,我们能够知道这具体是哪个品种的狗狗。
from keras.applications.resnet50 import preprocess_input, decode_predictions
def ResNet50_predict_labels(img_path):
# 返回img_path路径的图像的预测向量
img = preprocess_input(path_to_tensor(img_path))
return np.argmax(ResNet50_model.predict(img))
def dog_detector(img_path):
prediction = ResNet50_predict_labels(img_path)
return ((prediction <= 268) & (prediction >= 151))
dog_detect_humans = np.mean(list(map(dog_detector, human_files_short)))
print (dog_detect_humans)
dog_detect_dogs = np.mean(list(map(dog_detector, dog_files_short)))
print (dog_detect_dogs)
### TODO: 测试dog_detector函数在human_files_short和dog_files_short的表现
human3 = []
for i in human_files_short:
if dog_detector(i):
human3.append(i)
dog3 = []
for i in dog_files_short:
if dog_detector(i):
dog3.append(i)
print ("human {}, dog {}".format(len(human3), len(dog3)))
现在我们已经实现了一个函数,能够在图像中识别人类及狗狗。但我们需要更进一步的方法,来对狗的类别进行识别。在这一步中,你需要实现一个卷积神经网络来对狗的品种进行分类。你需要从头实现你的卷积神经网络(在这一阶段,你还不能使用迁移学习),并且你需要达到超过1%的测试集准确率。在本项目的步骤五种,你还有机会使用迁移学习来实现一个准确率大大提高的模型。
在添加卷积层的时候,注意不要加上太多的(可训练的)层。更多的参数意味着更长的训练时间,也就是说你更可能需要一个 GPU 来加速训练过程。万幸的是,Keras 提供了能够轻松预测每次迭代(epoch)花费时间所需的函数。你可以据此推断你算法所需的训练时间。
值得注意的是,对狗的图像进行分类是一项极具挑战性的任务。因为即便是一个正常人,也很难区分布列塔尼犬和威尔士史宾格犬。
| 布列塔尼犬(Brittany) | 威尔士史宾格犬(Welsh Springer Spaniel) |
|---|---|
![]() |
![]() |
不难发现其他的狗品种会有很小的类间差别(比如金毛寻回犬和美国水猎犬)。
| 金毛寻回犬(Curly-Coated Retriever) | 美国水猎犬(American Water Spaniel) |
|---|---|
![]() |
![]() |
同样,拉布拉多犬(labradors)有黄色、棕色和黑色这三种。那么你设计的基于视觉的算法将不得不克服这种较高的类间差别,以达到能够将这些不同颜色的同类狗分到同一个品种中。
| 黄色拉布拉多犬(Yellow Labrador) | 棕色拉布拉多犬(Chocolate Labrador) | 黑色拉布拉多犬(Black Labrador) |
|---|---|---|
![]() |
![]() |
![]() |
我们也提到了随机分类将得到一个非常低的结果:不考虑品种略有失衡的影响,随机猜测到正确品种的概率是1/133,相对应的准确率是低于1%的。
请记住,在深度学习领域,实践远远高于理论。大量尝试不同的框架吧,相信你的直觉!当然,玩得开心!
通过对每张图像的像素值除以255,我们对图像实现了归一化处理。
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
# Keras中的数据预处理过程
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255
创建一个卷积神经网络来对狗品种进行分类。在你代码块的最后,执行 model.summary() 来输出你模型的总结信息。
我们已经帮你导入了一些所需的 Python 库,如有需要你可以自行导入。如果你在过程中遇到了困难,如下是给你的一点小提示——该模型能够在5个 epoch 内取得超过1%的测试准确率,并且能在CPU上很快地训练。

在下方的代码块中尝试使用 Keras 搭建卷积网络的架构,并回答相关的问题。
回答: 1、conv2d:利用卷积(浓缩)提取特征 2、maxpooling:让模型取最大的一层,让模型更有侧重点(原理上也是根据权重来选择的),同时也可以提高效率 3、softmax:输出预测分类的概率;4、最后在测试集上得到1.9139%的准确率
下方的可选题,数据增强的,训练了不知道怎么写预测的代码?
# set up gpu
import keras.backend.tensorflow_backend as KTF
import tensorflow as tf
# # import layers, models
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D, BatchNormalization
from keras.layers import Dropout, Flatten, Dense, Activation
from keras.models import Sequential
# set up gpu
KTF.set_session(tf.Session(config=tf.ConfigProto(device_count={'gpu':0})))
model1 = Sequential()
### TODO: 定义你的网络架构
model1.add(Conv2D(16, 3, padding='valid', input_shape=(224, 224, 3)))
model1.add(MaxPooling2D(2))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Dropout(0.2))
model1.add(Conv2D(32, 3, padding='valid'))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Conv2D(32, 3, padding='valid'))
model1.add(MaxPooling2D(2))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Dropout(0.2))
model1.add(Conv2D(64, 3, padding='valid'))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Conv2D(64, 3, padding='valid'))
model1.add(MaxPooling2D(2))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Dropout(0.2))
model1.add(Conv2D(128, 3, padding='valid'))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Conv2D(128, 3, padding='valid'))
model1.add(MaxPooling2D(2))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Dropout(0.2))
model1.add(Conv2D(128, 3, padding='valid'))
model1.add(MaxPooling2D(2))
model1.add(BatchNormalization())
model1.add(Activation('relu'))
model1.add(Dropout(0.2))
model1.add(GlobalAveragePooling2D())
model1.add(Dense(133, activation='softmax'))
model1.summary()
import keras.optimizers as optimizers
adam = optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
model1.compile(optimizer=adam, loss='categorical_crossentropy', metrics=['accuracy'])
from keras.callbacks import ModelCheckpoint, EarlyStopping
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5', verbose=1, save_best_only=True)
earlystopping = EarlyStopping(min_delta=0.001, patience=20, verbose=1)
model1.fit(train_tensors, train_targets, validation_data=(valid_tensors, valid_targets),
epochs=100, batch_size=64, callbacks=[checkpointer, earlystopping,], verbose=1)
model1.load_weights('saved_models/weights.best.from_scratch.hdf5')
dog_breed_predictions = [np.argmax(model1.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]
test_accuracy = 100 * np.sum(np.array(dog_breed_predictions) == np.argmax(test_targets, axis=1)) / len(dog_breed_predictions)
print ('Test accuracy: %.4f%%' % test_accuracy)
## 编译模型
model_f.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
from keras.preprocessing.image import ImageDataGenerator
train_generator = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
test_generator = ImageDataGenerator(rescale=1./255)
batch_size = 20
tr_gen = train_generator.flow_from_directory('/data/dog_images/train', target_size=(224, 224), batch_size=batch_size)
va_gen = train_generator.flow_from_directory('/data/dog_images/valid', target_size=(224, 224), batch_size=batch_size)
te_gen = test_generator.flow_from_directory('/data/dog_images/test', target_size=(224, 224), batch_size=batch_size)
from keras.callbacks import ModelCheckpoint, EarlyStopping
### TODO: 设置训练模型的epochs的数量
epochs = 5
### 不要修改下方代码
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5',
verbose=1, save_best_only=True)
earlystopping = EarlyStopping(min_delta=0.001, patience=20, verbose=1)
model1.fit_generator(tr_gen,
steps_per_epoch=2000,
epochs=50,
validation_data=va_gen,
validation_steps=800,
callbacks=[checkpointer, earlystopping])
model_data_agumented = Sequential()
### TODO: 定义你的网络架构
model_data_agumented.add(Conv2D(16, 2, input_shape=(224, 224, 3)))
model_data_agumented.add(MaxPooling2D(2))
model_data_agumented.add(Conv2D(32, 2))
model_data_agumented.add(MaxPooling2D(2))
model_data_agumented.add(Conv2D(64, 2))
model_data_agumented.add(MaxPooling2D(2))
model_data_agumented.add(GlobalAveragePooling2D())
model_data_agumented.add(Dense(133, activation='softmax'))
model_data_agumented.summary()
model_data_agumented.save_weights('saved_models.first_data_augmented.hdf5')
## 加载具有最好验证loss的模型
model1.load_weights('saved_models/weights.best.from_scratch.hdf5')
在狗图像的测试数据集上试用你的模型。确保测试准确率大于1%。
# 获取测试数据集中每一个图像所预测的狗品种的index
dog_breed_predictions = [np.argmax(model1.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]
# 报告测试准确率
test_accuracy = 100 * np.sum(np.array(dog_breed_predictions)==np.argmax(test_targets, axis=1)) / len(dog_breed_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)
# initial model ---> build model ---> compile model ---> train model(model check and callback) ---> save and load the best model ---> predict
使用 迁移学习(Transfer Learning)的方法,能帮助我们在不损失准确率的情况下大大减少训练时间。在以下步骤中,你可以尝试使用迁移学习来训练你自己的CNN。
bottleneck_features = np.load('/data/bottleneck_features/DogVGG16Data.npz')
train_VGG16 = bottleneck_features['train']
valid_VGG16 = bottleneck_features['valid']
test_VGG16 = bottleneck_features['test']
该模型使用预训练的 VGG-16 模型作为固定的图像特征提取器,其中 VGG-16 最后一层卷积层的输出被直接输入到我们的模型。我们只需要添加一个全局平均池化层以及一个全连接层,其中全连接层使用 softmax 激活函数,对每一个狗的种类都包含一个节点。
VGG16_model = Sequential()
# VGG16_model.add(Conv2D(16, 3, padding='valid', input_shape=train_VGG16.shape[1:]))
# VGG16_model.add(MaxPooling2D())
# VGG16_model.add(BatchNormalization())
# VGG16_model.add(Activation('relu'))
VGG16_model.add(Conv2D(32, 3, padding='valid', input_shape=train_VGG16.shape[1:]))
VGG16_model.add(BatchNormalization())
VGG16_model.add(Activation('relu'))
VGG16_model.add(Dropout(0.2))
VGG16_model.add(Conv2D(32, 3, padding='valid'))
VGG16_model.add(MaxPooling2D())
VGG16_model.add(BatchNormalization())
VGG16_model.add(Activation('relu'))
VGG16_model.add(Dropout(0.2))
# VGG16_model.add(Conv2D(64, 3, padding='valid'))
# VGG16_model.add(BatchNormalization())
# VGG16_model.add(Activation('relu'))
# VGG16_model.add(Conv2D(64, 3, padding='valid'))
# VGG16_model.add(MaxPooling2D())
# VGG16_model.add(BatchNormalization())
# VGG16_model.add(Activation('relu'))
VGG16_model.add(GlobalAveragePooling2D())
VGG16_model.add(Dense(133, activation='softmax'))
VGG16_model.summary()
## 编译模型
from keras.optimizers import Adam
adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=10e-08, decay=0.0)
VGG16_model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy'])
## 训练模型
from keras.callbacks import ModelCheckpoint, EarlyStopping
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.VGG16.hdf5',
verbose=1, save_best_only=True)
earlystopping = EarlyStopping(min_delta=0.001, patience=10, verbose=1)
VGG16_model.fit(train_VGG16, train_targets,
validation_data=(valid_VGG16, valid_targets),
epochs=20, batch_size=20, callbacks=[checkpointer, earlystopping], verbose=1)
## 加载具有最好验证loss的模型
VGG16_model.load_weights('saved_models/weights.best.VGG16.hdf5')
现在,我们可以测试此CNN在狗图像测试数据集中识别品种的效果如何。我们在下方打印出测试准确率。
# 获取测试数据集中每一个图像所预测的狗品种的index
VGG16_predictions = [np.argmax(VGG16_model.predict(np.expand_dims(feature, axis=0))) for feature in test_VGG16]
# 报告测试准确率
test_accuracy = 100*np.sum(np.array(VGG16_predictions)==np.argmax(test_targets, axis=1))/len(VGG16_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)
from extract_bottleneck_features import *
def VGG16_predict_breed(img_path):
# 提取bottleneck特征
bottleneck_feature = extract_VGG16(path_to_tensor(img_path))
# 获取预测向量
predicted_vector = VGG16_model.predict(bottleneck_feature)
# 返回此模型预测的狗的品种
return dog_names[np.argmax(predicted_vector)]
现在你将使用迁移学习来建立一个CNN,从而可以从图像中识别狗的品种。你的 CNN 在测试集上的准确率必须至少达到60%。
在步骤4中,我们使用了迁移学习来创建一个使用基于 VGG-16 提取的特征向量来搭建一个 CNN。在本部分内容中,你必须使用另一个预训练模型来搭建一个 CNN。为了让这个任务更易实现,我们已经预先对目前 keras 中可用的几种网络进行了预训练:
这些文件被命名为为:
Dog{network}Data.npz
其中 {network} 可以是 VGG19、Resnet50、InceptionV3 或 Xception 中的一个。选择上方网络架构中的一个,他们已经保存在目录 /data/bottleneck_features/ 中。
在下方代码块中,通过运行下方代码提取训练、测试与验证集相对应的bottleneck特征。
bottleneck_features = np.load('/data/bottleneck_features/Dog{network}Data.npz')
train_{network} = bottleneck_features['train']
valid_{network} = bottleneck_features['valid']
test_{network} = bottleneck_features['test']
### TODO: 从另一个预训练的CNN获取bottleneck特征
bottleneck_features = np.load('/data/bottleneck_features/DogInceptionV3Data.npz')
train_Inception = bottleneck_features['train']
valid_Inception = bottleneck_features['valid']
test_Inception = bottleneck_features['test']
bn_fs_x = np.load('/data/bottleneck_features/DogXceptionData.npz')
tr_x = bn_fs_x['train']
va_x = bn_fs_x['valid']
te_x = bn_fs_x['test']
bn_fs_r = np.load('/data/bottleneck_features/DogResnet50Data.npz')
tr_r = bn_fs_r['train']
va_r = bn_fs_r['valid']
te_r = bn_fs_r['test']
bn_fs_v = np.load('/data/bottleneck_features/DogVGG19Data.npz')
tr_v = bn_fs_v['train']
va_v = bn_fs_v['valid']
te_v = bn_fs_v['test']
### TODO: 定义你的框架
def my_transfer_model(shape):
model = Sequential()
#model.add(GlobalAveragePooling2D(input_shape=train_Inception.shape[1:]))
model.add(Conv2D(32, 3, padding='valid', input_shape=shape))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(Conv2D(32, 3, padding='valid'))
model.add(MaxPooling2D())
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Dropout(0.2))
model.add(GlobalAveragePooling2D())
model.add(Dense(133, activation='softmax'))
print(model.summary())
return model
### TODO: 定义你的框架
def my_transfer_model2(shape):
model = Sequential()
#model.add(GlobalAveragePooling2D(input_shape=train_Inception.shape[1:]))
# model.add(Conv2D(64, 3, padding='valid', input_shape=shape))
# model.add(MaxPooling2D())
# model.add(BatchNormalization())
# model.add(Activation('relu'))
# model.add(Dropout(0.2))
model.add(GlobalAveragePooling2D(input_shape=shape))
model.add(Dense(133, activation='softmax'))
print(model.summary())
return model
### TODO: 编译模型
from keras.optimizers import Adam
def my_transfer_compile(model):
adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=10e-08, decay=0.0)
model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy'])
model_x = my_transfer_model(tr_x.shape[1:])
my_transfer_compile(model_x)
model_r = my_transfer_model2(tr_r.shape[1:])
my_transfer_compile(model_r)
model_v = my_transfer_model(tr_v.shape[1:])
my_transfer_compile(model_v)
### TODO: 训练模型
# 'saved_models/weights.best.InceptionV3.hdf5'
def my_transfer_fit(model, train, train_targets, validation, valid_targets, batch_size, epochs, savepath):
checkpoint = ModelCheckpoint(filepath=savepath, verbose=1, save_best_only=True)
earlystopping = EarlyStopping(min_delta=0.001, patience=10, verbose=1)
model.fit(train, train_targets, validation_data=(validation, valid_targets), verbose=1, batch_size=20, epochs=epochs,
callbacks=[checkpoint, earlystopping])
my_transfer_fit(model_x, tr_x, train_targets, va_x, valid_targets, 20, 50, 'saved_models/weights.best.Xception.hdf5')
model_x.load_weights('saved_models/weights.best.Xception.hdf5')
my_transfer_fit(model_r, tr_r, train_targets, va_r, valid_targets, 20, 50, 'saved_models/weights.best.Resnet.hdf5')
model_r.load_weights('saved_models/weights.best.Resnet.hdf5')
my_transfer_fit(model_v, tr_v, train_targets, va_v, valid_targets, 20, 50, 'saved_models/weights.best.VGG19.hdf5')
model_v.load_weights('saved_models/weights.best.VGG19.hdf5')
pre_x = [np.argmax(model_x.predict(np.expand_dims(feature, axis=0))) for feature in te_x]
acc_x = 100 * np.sum(np.array(pre_x) == np.argmax(test_targets, axis=1)) / len(pre_x)
print ('accuracy in transferring from xception is %.4f%%' % acc_x)
pre_r = [np.argmax(model_r.predict(np.expand_dims(feature, axis=0))) for feature in te_r]
acc_r = 100 * np.sum(np.array(pre_r) == np.argmax(test_targets, axis=1)) / len(pre_r)
print ('accuracy in transferring from resnet is %.4f%%' % acc_r)
pre_v = [np.argmax(model_v.predict(np.expand_dims(feature, axis=0))) for feature in te_v]
acc_v = 100 * np.sum(np.array(pre_v) == np.argmax(test_targets, axis=1)) / len(pre_v)
print ('accuracy in transferring from vgg19 is %.4f%%' % acc_v)
### TODO: 在测试集上计算分类准确率
def my_prediction_transfer(model, test_data, name):
prediction = [np.argmax(model.predict(np.expand_dims(feature, axis=0))) for feature in test_data]
accuracy = np.sum(np.array(prediction_inception) == np.argmax(test_targets, axis=1)) / len(prediction)
print('accuracy in transfering from % is %.4f%%' % (name, accuracy))
实现一个函数,它的输入为图像路径,功能为预测对应图像的类别,输出为你模型预测出的狗类别(Affenpinscher, Afghan_hound 等)。
与步骤5中的模拟函数类似,你的函数应当包含如下三个步骤:
dog_names 数组来返回对应的狗种类名称。提取图像特征过程中使用到的函数可以在 extract_bottleneck_features.py 中找到。同时,他们应已在之前的代码块中被导入。根据你选定的 CNN 网络,你可以使用 extract_{network} 函数来获得对应的图像特征,其中 {network} 代表 VGG19, Resnet50, InceptionV3, 或 Xception 中的一个。
### TODO: 写一个函数,该函数将图像的路径作为输入
### 然后返回此模型所预测的狗的品种
from extract_bottleneck_features import *
def predicted_name(img_path, model):
bottleneck_feature = extract_Xception(path_to_tensor(img_path))
predicted_vector = model.predict(bottleneck_feature)
return dog_names[np.argmax(predicted_vector)]
print(predicted_name(dog_files_short[0], model_x))
bottleneck_feature_x = extract_Xception(path_to_tensor(dog_files_short[1]))
predicted_vector_x = model_x.predict(bottleneck_feature_x)
print(dog_names[np.argmax(predicted_vector_x)])
bottleneck_feature_v = extract_VGG19(path_to_tensor(dog_files_short[1]))
predicted_vector_v = model_v.predict(bottleneck_feature_v)
print(dog_names[np.argmax(predicted_vector_v)])
### TODO: 设计你的算法
### 自由地使用所需的代码单元数吧
def human_dog_error(img_path, model):
result = ''
if dog_detector(img_path):
result = "This is a {} dog.".format(predicted_name(img_path, model))
elif face_detector(img_path):
result = "This is a human but has been recognized as a {}.".format(predicted_name(img_path, model))
else:
result = 'Error ocurred.'
return result
human_dog_error(dog_files_short[20], model_x)
在这个部分中,你将尝试一下你的新算法!算法认为你看起来像什么类型的狗?如果你有一只狗,它可以准确地预测你的狗的品种吗?如果你有一只猫,它会将你的猫误判为一只狗吗?
上传方式:点击左上角的Jupyter回到上级菜单,你可以看到Jupyter Notebook的右上方会有Upload按钮。
在下方编写代码,用至少6张现实中的图片来测试你的算法。你可以使用任意照片,不过请至少使用两张人类图片(要征得当事人同意哦)和两张狗的图片。 同时请回答如下问题:
## TODO: 在你的电脑上,在步骤6中,至少在6张图片上运行你的算法。
## 自由地使用所需的代码单元数吧
import os
my_images = os.listdir('my_predict_image/')
abs_path = os.path.abspath(os.curdir)
my_image_predictions = []
for i in my_images:
my_image_predictions.append(human_dog_error(abs_path + '/my_predict_image/' + i, model_x))
my_images
my_image_predictions
def display_my_image(file_path):
import matplotlib.pyplot as plt
% matplotlib inline
pixels = plt.imread(file_path)
plt.imshow(pixels)
plt.show()
for i in my_images:
display_my_image(abs_path + '/my_predict_image/' + i)
注意: 当你写完了所有的代码,并且回答了所有的问题。你就可以把你的 iPython Notebook 导出成 HTML 文件。你可以在菜单栏,这样导出File -> Download as -> HTML (.html)把这个 HTML 和这个 iPython notebook 一起做为你的作业提交。